#include <iostream>
#include <vector>
#include <sstream>
#include <fstream>

#include "CThread.h"
#include "CBlockStream.h"
//#include "CBufferedStream.h"

#define MAX_LINE_LIMIT 10000

using namespace std;


class CLineTokenizerThread : public CThread
{
private:
	CBlockStream *block_stream;

	int kpos;
	vector<int> ppos;
	char delim;
	char eol;
	long limit;

public:
	CLineTokenizerThread() {
	}
	virtual ~CLineTokenizerThread() {
		delete block_stream;
	}

	void run() {
		block_stream->resize(1024);

		char uniq_file[100];
		string thread_id = getName();
		sprintf(uniq_file, "xxx-%d-%s", getpid(), (char *)thread_id.c_str());
		ofstream fout(uniq_file, ios::app|ios::out);

		int key = kpos-1;
		vector< vector<string> > kv_vectors;
		while(block_stream->getline(kv_vectors, delim, limit)!=0) {
			for(int i=0;i<kv_vectors.size();i++) {
				//fout << "#[" << kv_vectors[i][key] << "]" << endl;

				//for(int j=0;j<ppos.size();j++) {
				for(int j=0;j<kv_vectors[i].size();j++) {
					if(j+1>=kv_vectors[i].size()) fout << kv_vectors[i][j];
					else fout << kv_vectors[i][j] << delim;
				}
				fout << endl;
			}

			kv_vectors.clear();
		}

		fout.close();
	}	

	void assign(long block, long blocks) {
		block_stream->assignBlock(block, blocks);
	}

	void set(char *file) {
		block_stream = new CBlockStream(file);

		kpos = 1;
		delim = '\t';
		eol = '\n';
		limit = MAX_LINE_LIMIT;
	}

	void set(vector<int> &ppos, int kpos, char delim, char eol, long limit) {
		for(int i=0;i<ppos.size();i++) this->ppos.push_back(ppos[i]);

		this->kpos = kpos;
		this->delim = delim;
		this->eol = eol;
		this->limit = limit;
	}
};

void print_out(CBlockStream *block_stream, vector<int> &ppos, int kpos, char delim, char eol, long limit)
{
	int key = kpos-1;
	vector< vector<string> > kv_vectors;
	while(block_stream->getline(kv_vectors, delim, limit)!=0) {
		for(int i=0;i<kv_vectors.size();i++) {
			//cout << "#[" << kv_vectors[i][key] << "]" << endl;

			//for(int j=0;j<ppos.size();j++) {
			for(int j=0;j<kv_vectors[i].size();j++) {
				if(j+1>=kv_vectors[i].size()) cout << kv_vectors[i][j];
				else cout << kv_vectors[i][j] << delim;
			}
			cout << endl;
		}

		kv_vectors.clear();
	}
}

int main(int argc,char *argv[])
{
	if(argc<2) {
		cout << "Usages : " << argv[0] << " -f <file> -k <pos> -d <delim> -e <eol> -p <pos to print> -b <block> -t <blocks> -1 -2" << endl;
		cout << "\t\t-1 : no use thread" << endl;
		cout << "\t\t-2 : use threads" << endl;
		exit(-1);
	}

	int opt;
	static char optstr[] = "fkpdebt12";

	char *file = NULL;
	int kpos = 1;
	vector<int> ppos;
	char delim = '\t';
	char eol = '\n';
	long limit = MAX_LINE_LIMIT;
	int block = 0;
	int blocks = 1;
	int mode = 1;

	while((opt=getopt(argc,argv,optstr))!=-1) {
		switch(opt) {
			case 'f' :
					file = argv[optind++];
					break;
			case 'k' :
					kpos = atoi(argv[optind++]);
					break;
			case 'p' :
					ppos.push_back(atoi(argv[optind++]));
					break;
			case 'd' :
					delim = (char) *(argv[optind++]);
					break;
			case 'e' :
					eol = (char) *(argv[optind++]);
					break;
			case 'b' :
					block = atoi(argv[optind++]);
					break;
			case 't' :
					blocks = atoi(argv[optind++]);
					break;
			case '1' :
					mode = 1;
					break;
			case '2' :
					mode = 2;
					break;
		}
	}

	if(file!=NULL) {
		if(mode==1) {
			CBlockStream block_stream(file);

			block_stream.resize(50);
			block_stream.assignBlock(block,blocks);
			print_out(&block_stream, ppos, kpos, delim, eol, limit);
		}
		else if(mode==2) {
			vector< CLineTokenizerThread *> tok_threads;

			for(int i=0;i<blocks;i++) {
				CLineTokenizerThread *thr = new CLineTokenizerThread();
				
				tok_threads.push_back(thr);

				tok_threads[i]->set(file);
				tok_threads[i]->set(ppos, kpos, delim, eol, limit);
				tok_threads[i]->assign(i, blocks);
				tok_threads[i]->start();
			}

			for(int i=0;i<blocks;i++) {
				tok_threads[i]->join();
				delete tok_threads[i];
			}
		}
	}

	else {
		CBlockStream block_stream(cin.rdbuf());

/*
		stringstream ss;
		for(int i=0;i<100;i++) {
			ss << i << '\t' << "woorung nara" << endl;
		}
		CBlockStream block_stream(ss.rdbuf());
*/


		//block_stream.assignBlock(block,blocks);

		print_out(&block_stream, ppos, kpos, delim, eol, limit);
	}
}
